package com.vecspace.dayu.code.source.service;

import com.vecspace.dayu.code.consts.CodeTemplateParamType;
import com.vecspace.dayu.code.pojo.DataColumn;
import com.vecspace.dayu.code.pojo.DataTable;
import com.vecspace.dayu.code.pojo.Func;
import com.vecspace.dayu.code.pojo.Project;
import com.vecspace.dayu.code.service.DataColumnService;
import com.vecspace.dayu.code.service.DataTableService;
import com.vecspace.dayu.code.service.FuncService;
import com.vecspace.dayu.code.service.ProjectService;
import com.vecspace.dayu.code.source.model.SourceCode;
import com.vecspace.dayu.code.source.model.SourceCodeParams;
import com.vecspace.dayu.code.source.model.SourceCodeParamsBaseTable;
import com.vecspace.dayu.code.source.model.TemplateFileInfo;
import com.vecspace.dayu.code.template.pojo.CodeFragment;
import com.vecspace.dayu.code.template.pojo.CodeTemplate;
import com.vecspace.dayu.code.template.service.CodeFragmentService;
import com.vecspace.dayu.code.template.service.CodeTemplateService;
import com.vecspace.dayu.code.ui.model.PageConfigVO;
import com.vecspace.dayu.code.ui.service.PageConfigService;
import com.vecspace.exception.HException;
import com.vecspace.utils.Assert;
import com.vecspace.utils.FreemarkerUtil;
import com.vecspace.utils.log.HLogger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

@Service("dayu_sourceCodeService")
public class SourceCodeService {

    /**
     *
     * @param funcId
     * @param terminalType
     * @param pageLayoutId
     * @param codeType
     * @param loginId
     * @return
     * @author wanghua
     * @date 2021-08-14
     */
    public List<SourceCode>  generateCodes(Long funcId, String terminalType, Long pageLayoutId, String codeType, String loginId) throws Exception {
        //获取配置参数
        SourceCodeParams sourceCodeParams = this.getSourceCodeParams(funcId,terminalType, loginId);

        //获取所有的FTL模板文件
        List<TemplateFileInfo> codeTemplates = getCodeTemplatesByLayoutFromDB(pageLayoutId, codeType);

        //根据模板和参数，生成实际的java代码
        List<SourceCode> javaCodeFiles = this.genServiceSourceCode(sourceCodeParams, codeTemplates);

        return javaCodeFiles;
    }

    /**根据配置参数和模板文件生成源代码
     *
     * @param sourceCodeParams
     * @param codeTemplates
     * @return
     * @throws Exception
     */
    private List<SourceCode> genServiceSourceCode(SourceCodeParams sourceCodeParams, List<TemplateFileInfo> codeTemplates) throws Exception {
        Assert.throwz(sourceCodeParams != null, "sourceCodeParams不能为null");
        Assert.throwz(codeTemplates != null, "codeTemplates不能为null");

        List<SourceCode> sourceCodeList = new ArrayList<>();
        for(TemplateFileInfo codeTemplate : codeTemplates){
            String fileParamType = codeTemplate.getFileParamType();
            if(CodeTemplateParamType.SingleTable.equals(fileParamType)){
                List<SourceCodeParamsBaseTable> tablesParam = sourceCodeParams.getSourceCodeParams();
                for(SourceCodeParamsBaseTable tableParam : tablesParam){
                    SourceCode srcCode = genSourceCode(codeTemplate, tableParam);
                    sourceCodeList.add(srcCode);
                }
            }else if(CodeTemplateParamType.MainPage.equals(fileParamType)){
                SourceCode srcCode = genSourceCode(codeTemplate, sourceCodeParams);
                sourceCodeList.add(srcCode);
            }else{
                throw new HException(String.format("codeTemplate.id=%d,FileParamType不能为空，只能取值:%s,%s", codeTemplate.getId(),CodeTemplateParamType.SingleTable,CodeTemplateParamType.MainPage));
            }
        }
        return sourceCodeList;
    }

    /**根据一个文件模板和模板参数，生成一个源代码文件
     *
     * @param codeTemplate
     * @param param
     * @return
     * @throws Exception
     */
    private SourceCode genSourceCode(TemplateFileInfo codeTemplate, Object param) throws Exception {
        String filePathTemplate = codeTemplate.getTemplateFileName();
        filePathTemplate = filePathTemplate.replace("##", File.separator);
        String codeTemplateText = codeTemplate.getTemplateCode();
        HLogger.DEFAULT().debug(filePathTemplate);
        try {
            String sourcePath = FreemarkerUtil.templateParser(filePathTemplate, param);
            //sourcePath = sourcePath.replace(".", File.separator);
            String sourceCode = FreemarkerUtil.templateParser(codeTemplateText, param);
            SourceCode srcCode = new SourceCode();
            srcCode.setFilePath(sourcePath);
            srcCode.setSrcCode(sourceCode);
            return srcCode;
        }catch (Exception ex){
            ex.printStackTrace();
            String msg1 = "";
            Throwable cause = ex.getCause();
            if(cause != null && cause instanceof InvocationTargetException) {
            	Throwable invoEx = ((InvocationTargetException)cause).getTargetException();            	
            	msg1 = invoEx.getMessage();
            	throw new HException(String.format("%s:%s",filePathTemplate, msg1));
            }
            throw new HException(String.format("%s:%s",filePathTemplate, ex.getMessage()));
        }
    }

    private SourceCodeParams getSourceCodeParams(Long funcId, String terminalType, String loginId) throws Exception {
        Func func = funcService.getFunc(funcId);
        Assert.throwz(func != null, "funcId不存在："+funcId);
        Project project = projectService.getProject(func.getProjectId());

        List<DataTable> tables = tableService.getDataTables(funcId);
        for(DataTable table : tables) {
            List<DataColumn> dataColumns = dataColumnService.getDataColumns(table.getId());
            table.setColumns(dataColumns);
        }

        List<PageConfigVO> tableConfigs = new ArrayList<>();
        for(DataTable table : tables) {
            PageConfigVO tableConfig = pageConfigService.getPageConfigVOByTable(table.getId(), terminalType);
            tableConfig.setTable(table);
            tableConfigs.add(tableConfig);
        }

        SourceCodeParams sourceCodeParams = new SourceCodeParams();
        sourceCodeParams.initParams(loginId, project, func, tables, tableConfigs);

        sourceCodeParams.checkParams();

        return sourceCodeParams;
    }

    /**获取一个布局类型下的所有页面模板（返回的模板中代码片段已经经过替换处理）
     *
     * @param layoutId
     * @return
     * @throws Exception
     * @author wanghua
     */
    public List<TemplateFileInfo> getCodeTemplatesByLayoutFromDB(Long layoutId, String codeType) throws Exception{
        List<TemplateFileInfo> fileList = new ArrayList<>();
        //获取指定布局以及通用终端布局下代码类型为codetype的模板.通用布局一般指后端代码模板
        List<CodeTemplate> codeTemplates = codeTemplateService.getCodeTemplatesByCodeTypeWithCommon(layoutId, codeType);
        List<CodeFragment> codeFragments = codeFragmentService.getAllCodeFragments();
        for(CodeTemplate codeTemplate : codeTemplates) {
            //替换模板中的子模板
            String codeTemplateText = codeTemplate.getCodeTemplateText();
            if(codeTemplateText == null || codeTemplateText.length() == 0)continue;
            for(CodeFragment codeFragment : codeFragments) {//子模板
                String fragmentCode = codeFragment.getCodeTemplate();
                if(fragmentCode == null) fragmentCode = "";
                String code = codeFragment.getCode();
                if(code == null || code.trim().length() == 0)continue;
                String holdTag = "{{{" + code.trim().toUpperCase() +"}}}";
                codeTemplateText = codeTemplateText.replace(holdTag, fragmentCode);
            }

            TemplateFileInfo fileInfo = new TemplateFileInfo();
            fileInfo.setId(codeTemplate.getId());
            fileInfo.setCodeType(codeTemplate.getCodeType());
            fileInfo.setFileParamType(codeTemplate.getFileParamType());
            fileInfo.setFilePath(codeTemplate.getFilePath());
            fileInfo.setTemplateCode(codeTemplateText);
            fileList.add(fileInfo);
        }
        return fileList;
    }

    @Autowired
    ProjectService projectService;

    @Autowired
    FuncService funcService;

    @Autowired
    DataTableService tableService;

    @Autowired
    DataColumnService dataColumnService;

    @Autowired
    PageConfigService pageConfigService;

    @Autowired
    private CodeTemplateService codeTemplateService;

    @Autowired
    private CodeFragmentService codeFragmentService;
}
